home *** CD-ROM | disk | FTP | other *** search
- /*
- SNEWS 1.91
-
- snews - a simple threaded news reader
-
-
- Copyright (C) 1991 John McCombs, Christchurch, NEW ZEALAND
- john@ahuriri.gen.nz
- PO Box 2708, Christchurch, NEW ZEALAND
-
- Modifications copyright (C) 1993 Daniel Fandrich
- <dan@fch.wimsey.bc.ca> or CompuServe 72365,306
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 1, as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- See the file COPYING, which contains a copy of the GNU General
- Public License.
-
-
- Source is formatted with a tab size of 4.
- */
-
- #include "defs.h"
- #include "snews.h"
- #include "pccharst.h"
- #include "getopt.h"
- #include <alloc.h>
- #include <ctype.h>
- #include <process.h>
-
- unsigned _stklen = 8192; /* default 4k stack can give problems */
-
- INFO my_stuff;
-
- /*------------------------------- main --------------------------------*/
- int main(int argc, char *argv[])
- {
- ACTIVE *gp, *head;
- int done, option;
- int verbose = 1, errflag = 0;
-
- signal(SIGINT, sig_break); /* turn control-break off */
-
- if (getenv("BIOSVIDEO") ||
- (getenv("TERM") && (stricmp(getenv("TERM"), "pcbios") == 0)))
- directvideo = 0;
-
- select_code_page();
-
- while ((option = getopt(argc, argv, "bqv?")) != -1)
- switch (option) {
- case 'b': /* bios video output */
- directvideo = 0;
- break;
-
- case 'q': /* quiet mode */
- verbose = 0;
- break;
-
- case 'v': /* verbose mode */
- ++verbose;
- break;
-
- case '?': /* display help */
- ++errflag;
- break;
- };
-
- if ((argc > 1) && (argv[1][0] == '/'))
- ++errflag;
-
- if (verbose)
- printf(VERSION "\n"
- "type snews -? for usage\n\n");
- if ((verbose >= 2) || errflag)
- printf(
- "Copyright (C) 1991 John McCombs\n"
- "Copyright (C) 1992 Michael Studte/John Dennis (Shinohara Industries)\n"
- "Copyright (C) 1992 Kai Uwe Rommel\n"
- "Copyright (C) 1993 Daniel Fandrich\n\n"
-
- "SNEWS comes with ABSOLUTELY NO WARRANTY.\n"
- "This is free software, and you are welcome to redistribute it\n"
- "under certain conditions; see the file COPYING for details.\n\n");
-
- if (errflag) {
- printf(
- "usage: snews [-q] [-v] [-b]\n"
- " -q set quiet mode\n"
- " -v set verbose mode\n"
- " -b use BIOS for video output\n"
- );
- return 2;
- }
-
- if (verbose)
- printf("loading config... ");
- if (load_stuff()) {
-
- if (verbose)
- printf("loading active... ");
- head = load_active_file();
- if (verbose)
- printf("loading read list... ");
- load_read_list();
- if (verbose)
- printf("loading history... ");
- load_history_list();
-
- done = FALSE;
- gp = NULL;
-
- while (!done) {
- if ((gp = select_group(head, gp)) != NULL) {
- done |= read_group(gp);
- } else {
- done = TRUE;
- }
- }
-
- clrscr();
-
- free_hist_list();
- if (verbose)
- printf("writing read list... ");
- save_read_list();
- if (verbose)
- printf("done\n");
- close_active_file();
- } else {
- fprintf(stderr, "Couldn't find neccessary item in the .rc files\n");
- return 2;
- }
- return 0;
- }
-
-
-
-
- /*-------------------------- find which group to read -----------------------*/
- ACTIVE *select_group(ACTIVE *head, ACTIVE *current)
- {
- /*
- * Present the list of groups, and allow him to move up and down with
- * the arrow and PgUp and PgDn keys. 'h' for help, -/+ for searches
- */
-
- ACTIVE *top; /* newsgroup at the top of the page */
- ACTIVE *this; /* current newsgroup */
- ACTIVE *tmp_ng;
- enum exit_codes exit_code; /* why we are exiting the loop */
- char sub_tmp[80];
-
- int i, j, articles, unread;
-
- this = (current == NULL) ? head : current;
-
- top = head;
- exit_code = EX_CONT;
-
- show_groups(&top, this, TRUE);
-
- while (exit_code == EX_CONT) {
-
- switch (get_any_key()) {
-
- case Fn1 :
- case '?' :
- case 'h' :
- show_help(HELP_GROUP);
- show_groups(&top, this, TRUE);
- break;
-
- case Fn2 :
- show_values();
- show_groups(&top, this, TRUE);
- break;
-
- case UP_ARR :
- if (this->last != NULL) this = this->last;
- break;
-
- case DN_ARR :
- if (this->next != NULL) this = this->next;
- break;
-
- case PGUP :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->last == NULL) break;
- this = this->last;
- }
- break;
-
- case PGDN :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->next == NULL) break;
- this = this->next;
- }
- break;
-
- case '1' :
- case HOME :
- top = this = head;
- show_groups(&top, this, TRUE);
- break;
-
- case '$' :
- case END :
- this = head;
- while (this->next != NULL)
- this = this->next;
- show_groups(&top, this, TRUE);
- break;
-
- case '/' :
- case '+' :
- this = search_groups(this);
- break;
-
- case 'p' :
- strcpy(sub_tmp, "");
- post(NULL, this->group, sub_tmp);
- show_groups(&top, this, TRUE);
- break;
-
- case 'c' :
- mark_group_as_read(this);
- show_groups(&top, this, TRUE);
- break;
-
- case ' ' :
- case 'n' :
- case TAB :
- tmp_ng = this->next;
- unread = 0;
- while (tmp_ng != NULL) {
- articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
- for (j = 0; j < articles; j++) {
- if ( *((tmp_ng->read_list)+j) == FALSE)
- unread++;
- }
- if (unread > 0) break;
- tmp_ng = tmp_ng->next;
- }
- if (unread > 0) {
- this = tmp_ng;
- } else {
- message("-- No more articles to read --");
- }
- break;
-
- case '!' :
- textbackground(BLACK); textcolor(LIGHTGRAY);
- cprintf("\r\n");
- spawnl(P_WAIT, getenv("COMSPEC"), getenv("COMSPEC"), NULL);
- show_groups(&top, this, TRUE);
- break;
-
- case ENTER :
- exit_code = EX_DONE;
- break;
-
- case 'q' :
- case ESCAPE :
- exit_code = EX_QUIT;
- break;
- };
- if (exit_code == EX_CONT)
- show_groups(&top, this, FALSE);
- }
-
- if (exit_code == EX_DONE)
- return(this);
- else
- return(NULL);
-
- }
-
-
-
- /*---------------------------- help screen ----------------------------------*/
- void show_help(int h)
- {
-
- char *type[] = {"New Group", "Thread", "Article"};
-
- textbackground(helpb); textcolor(helpf);
- clrscr();
- textbackground(headb); textcolor(headf);
- clreol();
- cprintf(" %s Help (%s)\r\n", type[h], VERSION);
- clreol();
- textbackground(helpb); textcolor(helpf);
-
- switch (h) {
- case HELP_GROUP :
-
- cprintf("\r\n\r\n");
- cprintf(" LIST NEWSGROUPS CHOOSING NEWSGROUPS\r\n\r\n");
- cprintf(" PgUp move display up one page TAB go to next unread newsgroup\r\n");
- cprintf(" PgDn move display down one page ENTER read selected newsgroup\r\n");
- cprintf(" Home move to top of list\r\n");
- cprintf(" End move to bottom of list\r\n");
- cprintf(" \x18 move up one line\r\n");
- cprintf(" \x19 move down one line\r\n");
- cprintf(" / search for text in group name\r\n\r\n");
- /* 1 2 */
- cprintf(" POST ARTICLE SPECIAL ACTIONS\r\n\r\n");
- cprintf(" p post article in newsgroup c mark this newsgroup as read\r\n");
- cprintf(" F2 show user values and info\r\n");
- cprintf(" ! shell to DOS (EXIT to return)\r\n");
-
- break;
-
- case HELP_THREAD :
- /* 1 2 */
- cprintf("\r\n\r\n");
- cprintf(" LISTING THREADS READING THREADS\r\n\r\n");
-
- cprintf(" PgUp move display up one page TAB read next unread article\r\n");
- cprintf(" PgDn move display down one page ENTER read the selected thread\r\n");
- cprintf(" Home move to top of list \x1a read the selected thread\r\n");
- cprintf(" End move to bottom of list BACKSP read final article in thread\r\n");
- cprintf(" \x18 move up one line\r\n");
- cprintf(" \x19 move down one line\r\n");
- cprintf(" + search for text in subject\r\n\r\n");
-
- cprintf(" MISCELLANEOUS ACTIONS EXTRACT THREADS\r\n\r\n");
- cprintf(" p post article in newsgroup s save thread to disk\r\n");
- cprintf(" c mark this newsgroup as read w append article to extract file\r\n");
- cprintf(" F2 show user values and info\r\n");
- cprintf(" F3 cycle through character sets\r\n");
- cprintf(" ! shell to DOS (EXIT to return)\r\n");
-
- break;
-
- case HELP_ARTICLES:
-
- cprintf("\r\n\r\n");
- cprintf(" READING ARTICLES CHOOSING ARTICLES\r\n");
- cprintf(" PgUp move display up one page TAB read next unread article\r\n");
- cprintf(" PgDn move display down one page ENTER read next article\r\n");
- cprintf(" Home move to top of article \x1a read next article in thread\r\n");
- cprintf(" End move to bottom of article \x1b read prior article in thread\r\n");
- cprintf(" \x18 move up one line ctl-\x1d skip to 10th article\r\n");
- cprintf(" \x19 move down one line\r\n");
- cprintf(" / search for text in article SEND MAIL\r\n");
- cprintf(" r mail reply to author\r\n");
- cprintf(" POST ARTICLE R mail reply to someone\r\n");
- cprintf(" p post new article m mail article to someone\r\n");
- cprintf(" f post follow-up article\r\n");
- cprintf(" SPECIAL ACTION\r\n");
- cprintf(" EXTRACT ARTICLES x decode ROT-13 article\r\n");
- cprintf(" s save article to file c mark all articles as read\r\n");
- cprintf(" w append article to extract file F2 show user values and info\r\n");
- cprintf(" | pipe article through command F3 cycle through character sets\r\n");
- cprintf(" F4 pipe article to hotpipe command ! shell to DOS (EXIT to return)\r\n");
- break;
- };
-
- message("-- Press any key to continue --");
- get_any_key();
-
- }
-
- /*-------------- show the values of user defined variables -------------*/
- void show_values(void)
- {
- textbackground(helpb); textcolor(helpf);
- clrscr();
- textbackground(headb); textcolor(headf);
- clreol();
- cprintf(" User values and info (%s)\r\n", VERSION);
- clreol();
- textbackground(helpb); textcolor(helpf);
- cprintf("\r\n\r\n");
-
- cprintf(" User Name: %s@%s (%s)\r\n\r\n",my_stuff.user, my_stuff.my_domain,
- my_stuff.my_name);
-
- cprintf(" Local node name: %s\r\n",my_stuff.my_site);
- cprintf(" Mail Server: %s\r\n",my_stuff.mail_server);
- cprintf(" Organization: %s\r\n\r\n",my_stuff.my_organisation);
-
- cprintf(" Signature File: %s\r\n",my_stuff.signature);
- cprintf(" Reply-To: %s\r\n",my_stuff.replyuser);
- cprintf(" Aliases file: %s\r\n",my_stuff.alias_file);
- cprintf(" Extract file: %s\r\n",my_stuff.extract_file);
- cprintf(" File Editor: %s\r\n",my_stuff.editor);
- cprintf(" F4 hotkey pipe to: %s\r\n\r\n",my_stuff.hotpipe);
-
- cprintf(" News Home Directory: %s\r\n",my_stuff.home);
- cprintf(" Temporary Directory: %s\r\n\r\n",my_stuff.temp_str);
-
- cprintf(" Active code page: %d%s\r\n", active_code_page,
- (code_page_table == CP_NOT_SUPP) ? " (unsupported)" : "");
- cprintf(" Last article charset: %s\r\n", char_set_names[current_char_set]);
-
- message("-- Press any key to continue --");
- get_any_key();
-
- }
-
- /*-------------------- show the list of active groups -----------------------*/
- void show_groups(ACTIVE **top, ACTIVE *this, int force)
- {
- /*
- * This routine takes 'top', a pointer to the first line on the screen
- * and 'this' a pointer to where we want to be, and updates the screen.
- * A maker to this is maintained, and the screen is repainted, where
- * necessary
- */
-
- static last_y;
- static last_index;
- int i, ur;
- ACTIVE *that;
-
- /*
- * If 'this' is above the 'top' or it is more than a screen length below,
- * or'this and 'top' are both zero, ie first time repaint the screen
- */
- if ( force || ((*top)->index > this->index) || (this->index - (*top)->index) > PAGE_LENGTH-1) {
- if (force) {
- textbackground(textb); textcolor(textf);
- clrscr();
- textbackground(headb); textcolor(headf);
- clreol();
- #ifdef __OS2__
- cprintf(" Select Newsgroup (%s)\r\n", VERSION);
- #else /* __MSDOS__ */
- cprintf(" Select Newsgroup (%s) [%ldk]\r\n", VERSION, farcoreleft()/1024);
- #endif
- clreol();
- cprintf("\r\n");
- clreol();
- }
- textbackground(textb); textcolor(textf);
-
- /* now adjust the top */
- *top = this;
- for (i = 0; i < (force ? PAGE_LENGTH/2 : last_y); i++) {
- if ((*top)->last == NULL) break;
- *top = (*top)->last;
- }
-
- that = *top;
- for (i = 0; i < PAGE_LENGTH; i++) {
- gotoxy(7, i + PAGE_HEADER);
-
- if (that != NULL) {
- ur = count_unread_in_group(that);
- cprintf("%4d. %s", ((*top)->index)+i+1, that->group);
- if (that->local)
- cprintf(" (local)");
- clreol();
- gotoxy(64, i + PAGE_HEADER);
- cprintf("%4.0d (%d)", ur, that->hi_num - that->lo_num);
- that = that->next;
- } else
- clreol();
- }
-
- gotoxy(5, last_y + PAGE_HEADER);
- last_y = this->index - (*top)->index;
- last_index = this->index;
-
- } else {
- textbackground(textb); textcolor(textf);
- gotoxy(5, last_y + PAGE_HEADER);
- }
- putch(' '); /* erase previous > character */
-
- last_y += (this->index - last_index);
- gotoxy(5, last_y + PAGE_HEADER);
- putch('>');
- last_index = this->index;
-
- command("ESC=quit TAB=next unread group ENTER=read group F1=help");
- textbackground(BLACK); textcolor(LIGHTGRAY); /* in case we exit program */
- }
-
- /*------------------------- search groups for text --------------------------*/
- ACTIVE *search_groups(ACTIVE *this)
- {
- char search_text[80];
- static char last_text[80] = {'\0'};
-
- message("Search for? ");
- if (*gets(search_text))
- strcpy(last_text, search_text);
- else
- strcpy(search_text, last_text);
-
- if (search_text[0])
- while (this->next) {
- this = this->next;
- if (stristr(this->group, search_text))
- break;
- }
-
- message("");
- return this;
- }
-
-
-
-
-
- /*---------------------------- process group --------------------------------*/
- int read_group(ACTIVE *gp)
- {
- /*
- * We now have newsgroup. Access the directory and try to read
- * the newsgroup articles, extracting the headers.
- */
-
- ARTICLE *start;
-
- if (gp->lo_num < gp->hi_num) {
- textbackground(textb); textcolor(textf);
- clrscr();
- textbackground(headb); textcolor(headf);
- clreol();
- #ifdef __OS2__
- cprintf(" Select Thread (%s)\r\n", VERSION);
- #else /* __MSDOS__ */
- cprintf(" Select Thread (%s) [%ldk]\r\n", VERSION, farcoreleft()/1024);
- #endif
- clreol();
- cprintf("Group: %s\r\n", gp->group);
- clreol();
- textbackground(textb); textcolor(textf);
-
- start = get_headers(gp);
- select_thread(gp, start);
- free_header(start);
- }
-
- return(0);
- }
-
-
- /*------------------- show the list of available threads --------------------*/
- void show_threads(ACTIVE *gp, ARTICLE **top, ARTICLE *this, int force)
- {
- /*
- * This routine takes 'top', a pointer to the first line on the screen
- * and 'this' a pointer to where we want to be, and updates the screen.
- * A maker to this is maintained, and the screen is repainted, where
- * necessary
- */
-
- static last_y;
- static last_index;
- int i;
- ARTICLE *that;
- int unread;
-
- /*
- * If 'this' is above the 'top' or it is more than a screen length below,
- * or 'this' and 'top' are both zero (i.e. first time), repaint the screen
- */
- if ( force || ((*top)->index > this->index) || (this->index - (*top)->index) > PAGE_LENGTH-1) {
- if (force) {
- textbackground(textb); textcolor(textf);
- clrscr();
- textbackground(headb); textcolor(headf);
- clreol();
- #ifdef __OS2__
- cprintf(" Select Thread (%s)\r\n", VERSION);
- #else /* __MSDOS__ */
- cprintf(" Select Thread (%s) [%ldk]\r\n", VERSION, farcoreleft()/1024);
- #endif
- clreol();
- cprintf("Group: %-57s %5d articles\r\n", gp->group, gp->hi_num - gp->lo_num);
- clreol();
- gotoxy(66,3);
- cprintf("%5d unread\r\n", count_unread_in_group(gp));
- }
- textbackground(textb); textcolor(textf);
-
- /* now adjust the top -- center selected thread when force is true */
- *top = this;
-
- for (i = 0; i < (force ? PAGE_LENGTH/2 : last_y); i++) {
- if ((*top)->last == NULL) break;
- *top = (*top)->last;
- }
-
- that = *top;
- for (i = 0; i < PAGE_LENGTH; i++) {
- gotoxy(4, i + PAGE_HEADER);
- if (that != NULL) {
- unread = count_unread_in_thread(gp, that),
- cprintf("%4d.%5.0d %4d %s", ((*top)->index)+i+1, unread, that->num_articles,
- translate_header(that->header, current_char_set));
- that = that->next;
- }
- clreol();
- }
-
- gotoxy(3, last_y + PAGE_HEADER);
- last_y = this->index - (*top)->index;
- last_index = this->index;
-
- } else {
- textbackground(textb); textcolor(textf);
- gotoxy(3, last_y + PAGE_HEADER);
- }
-
- putch(' '); /* erase previous > character */
-
- last_y += (this->index - last_index);
- gotoxy(3, last_y + PAGE_HEADER);
- putch('>');
- last_index = this->index;
-
- command("ESC=select group TAB=next unread ENTER=next article F1=help");
- }
-
-
-
-
- /*-------------------------- find which group to read -----------------------*/
- void select_thread(ACTIVE *gp, ARTICLE *head)
- {
- /*
- * Present the list of threads, and allow him to move up and down with
- * the arrow and PgUp and PgDn keys. 'h' for help, {-/+ for searches}
- */
-
- ARTICLE *top; /* thread at the top of the page */
- ARTICLE *this; /* current thread */
- ARTICLE *th;
- ART_ID *art;
- enum exit_codes exit_code; /* why we are exiting the loop */
- char sub_tmp[80];
-
- int i, idx, hit;
- int a_ct; /* position of current article in thread */
-
- this = head;
-
- top = head;
- exit_code = EX_CONT;
-
- show_threads(gp, &top, this, TRUE);
-
- while (exit_code == EX_CONT) {
-
- switch (get_any_key()) {
-
- case Fn1 :
- case '?' :
- case 'h' :
- show_help(HELP_THREAD);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case Fn2 :
- show_values();
- show_threads(gp, &top, this, TRUE);
- break;
-
- case Fn3 :
- if (current_char_set++ == US_ASCII)
- current_char_set = 0;
- show_threads(gp, &top, this, TRUE);
- break;
-
- case '[' :
- case UP_ARR :
- if (this->last != NULL) this = this->last;
- break;
-
- case 'n' :
- case ']' :
- case DN_ARR :
- if (this->next != NULL) this = this->next;
- break;
-
- case '<' :
- case PGUP :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->last == NULL) break;
- this = this->last;
- }
- break;
-
- case '>' :
- case PGDN :
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this->next == NULL) break;
- this = this->next;
- }
- break;
-
- case RIGHT :
- read_thread(gp, this, this->art_num, 1);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case '^' :
- case HOME :
- top = this = head;
- show_threads(gp, &top, this, TRUE);
- break;
-
- case '$' :
- case END :
- this = head;
- while (this->next != NULL)
- this = this->next;
- show_threads(gp, &top, this, TRUE);
- break;
-
- case 's' :
- save_thread_to_disk(gp, this, NULL);
- break;
-
- case 'w' :
- save_thread_to_disk(gp, this, my_stuff.extract_file);
- break;
-
- case 'p' :
- strcpy(sub_tmp, "");
- post(NULL, gp->group, sub_tmp);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case '!' :
- textbackground(BLACK); textcolor(LIGHTGRAY);
- cprintf("\r\n");
- spawnl(P_WAIT, getenv("COMSPEC"), getenv("COMSPEC"), NULL);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case ' ' :
- case TAB :
- /*
- * Go to the next unread article. Work through each
- * thread, looking at each article to see if it's been
- * read
- */
-
- /* for each thread */
- th = this;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int)(art->id - gp->lo_num - 1);
- a_ct++;
- if ( *((gp->read_list)+idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit) break;
- th = th->next;
- }
-
- if (hit) {
- this = th;
- read_thread(gp, this, art, a_ct);
- show_threads(gp, &top, this, TRUE);
- } else {
- message("-- No more articles to read --");
- }
- break;
-
- case 'c' :
- if (!mark_group_as_read(gp))
- exit_code = EX_QUIT; /* quit to newsgroup menu */
- else
- show_threads(gp, &top, this, TRUE);
- break;
-
- case ENTER :
- read_thread(gp, this, this->art_num, 1);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case BACKSP :
- art = this->art_num;
- a_ct = 1;
- while (art->next_art != NULL) {
- a_ct++;
- art = art->next_art;
- }
- read_thread(gp, this, art, a_ct);
- show_threads(gp, &top, this, TRUE);
- break;
-
- case '/' :
- case '+' :
- this = search_subjects(this);
- show_threads(gp, &top, this, FALSE);
- break;
-
- case ESCAPE :
- exit_code = EX_QUIT;
- break;
- };
- if (exit_code == EX_CONT)
- show_threads(gp, &top, this, FALSE);
- }
- }
-
-
-
-
- /*------------------------ search subjects for text -------------------------*/
- ARTICLE *search_subjects(ARTICLE *this)
- {
- char search_text[80];
- static char last_text[80] = {'\0'};
-
- message("Search for? ");
- if (*gets(search_text))
- strcpy(last_text, search_text);
- else
- strcpy(search_text, last_text);
-
- if (search_text[0])
- while (this->next) {
- this = this->next;
- if (stristr(translate_header(this->header, current_char_set), search_text))
- break;
- }
-
- message("");
- return this;
- }
-
-
- /*------------------------ save a thread ------------------------------*/
- void save_thread_to_disk(ACTIVE *gp, ARTICLE *this, char *save_name)
- {
- ART_ID *id;
- char *fn;
- TEXT *tx;
- LINE *ln;
- int a_ct;
- char fnx[81];
- int ch;
- FILE *tmp = NULL;
-
- if (save_name == NULL) { /* save function */
- lmessage("Filename? ");
- gets(fnx);
- } else
- strcpy(fnx, save_name); /* extract function */
-
- if (!*fnx) { /* file name not given */
- message("");
- return;
- }
- /* if file name starts with ~/ change to user's home directory */
- if ((fnx[0] == '~') && ((fnx[1] == '/') || (fnx[1] == '\\')) && fnx[2]) {
- memmove(fnx+strlen(my_stuff.home), fnx+2, strlen(fnx+2) + 1);
- memmove(fnx, my_stuff.home, strlen(my_stuff.home));
- }
-
- if (save_name) { /* extract function */
- if (access(fnx, 0) == 0) {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** Cannot open file for appending - "
- "please any key to continue ***");
- get_any_key();
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - press any key to continue ***");
- get_any_key();
- }
- }
- }
- else { /* save function */
- if (access(fnx, 0) == 0) {
- message("File exists - append (y/n)? ");
- while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
- if (ch == 'y') {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** Cannot open file for appending - "
- "please any key to continue ***");
- get_any_key();
- }
- }
- }
- else {
- /* make sure the file name given doesn't have bad characters */
- if ((strcspn(fnx, " \"*+,;<=>?[]|\x7f\xe5") != strlen(fnx)) ||
- ((tmp = fopen(fnx, "wt")) == NULL)) {
- message("*** Cannot open file for output - press any key to continue ***");
- get_any_key();
- }
- }
- }
-
- if (tmp != NULL) {
-
- fn = make_news_group_name(gp->group);
-
- id = this->art_num;
- a_ct = 0;
-
- while (id != NULL) {
- if (save_name) { /* extract function */
- fputs("\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\n",tmp);
- }
-
- tx = load_article(fn, id->art_off);
-
- ln = tx->top;
- while (ln != NULL) {
- fputs(ln->data, tmp);
- ln = ln->next;
- }
- fputs("\n", tmp);
- a_ct++;
- id = id->next_art;
- free_article(tx);
- }
- fclose(tmp);
- }
-
- message("");
- }
-
-
-
- /*------------------------ read a thread ------------------------------*/
- enum exit_codes read_thread(ACTIVE *gp, ARTICLE *this, ART_ID *first, int a_ct)
- {
- ART_ID *id;
- char *fn;
- int idx, cnt;
- enum exit_codes res = EX_CONT;
- TEXT *tx;
- char author[WHO_LENGTH], msg_id[MSG_ID_LENGTH];
- CROSS_POSTS *h, *h0;
- ACTIVE *gx;
-
-
- fn = make_news_group_name(gp->group);
-
- id = first;
-
- while ((id != NULL) && (res != EX_QUIT)) {
-
- tx = load_article(fn, id->art_off);
-
- res = read_article(gp, tx, this->header, a_ct, this->num_articles);
-
- /* mark this article as read */
- idx = (int) ((id->id) - gp->lo_num - 1);
- *((gp->read_list)+idx) = TRUE;
-
- /* mark the crossposts */
- get_his_stuff(tx, author, msg_id);
- if ((h0 = look_up_history(msg_id, gp->group)) != NULL) {
-
- h = h0;
- while (h != NULL) {
- gx = find_news_group(h->group);
- if (gx) {
- idx = (int) ((h->art_num) - gx->lo_num - 1);
- *((gx->read_list)+idx) = TRUE;
- }
- h = h->next;
- }
-
- free_cross_post_list(h0);
- }
-
- switch (res) {
- case EX_PREVIOUS: /* go to previous article */
- a_ct--;
- id = id->prev_art;
- break;
-
- case EX_PREVIOUS10: /* skip past previous 9 articles */
- for (cnt = 0; (cnt < 10) && id->prev_art; ++cnt) {
- a_ct--;
- id = id->prev_art;
- }
- break;
-
- case EX_NEXT: /* go to next article */
- a_ct++;
- id = id->next_art;
- break;
-
- case EX_NEXT10: /* skip past next 9 articles */
- for (cnt = 0; (cnt < 10) && id->next_art; ++cnt) {
- a_ct++;
- id = id->next_art;
- }
- break;
-
- case EX_NEXT_UNREAD:
- while (id != NULL) {
- idx = (int)(id->id - gp->lo_num - 1);
- if ( *((gp->read_list)+idx) == FALSE)
- break;
- a_ct++;
- id = id->next_art;
- }
- if (id == NULL)
- message("-- No more articles in thread --");
- break;
-
- } /* switch */
-
- free_article(tx);
-
- } /* while */
-
- return(res);
- }
-
-
- /*------------- compare multipart headers using some intelligence -----------*/
- /* These routines are modified from snews 2.0 (OS/2 version) patches by Kai
- Uwe Rommel */
- static int strip_off_part(char *str)
- {
- char *ptr = str + strlen(str); /* point to terminating null */
-
- /* strip from the end of the line (case-insensitive) things like:
- - "Part01/10"
- - "Part 01/10"
- - "Part 01 of 10"
- - "[1/10]"
- - "(1 of 10)"
- - "1 of 10"
- - "Patch02a/04"
- - "Patch20"
- returns the length of the string without the part number section,
- or 0 if the string doesn't have part numbers
- */
-
- /* kill trailing spaces */
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- /* kill trailing brackets */
- if ( ptr > str && (ptr[-1] == ')' || ptr[-1] == ']') )
- ptr--;
-
- /* kill second part number */
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- /* abort if there isn't a part number to the string */
- if ( !isdigit(*ptr) )
- return 0; /* header is not part of a multipart subject */
-
- /* kill /, of, Patch */
- if ( ptr > str && ptr[-1] == '/' )
- ptr--;
- else if ( ptr > str + 3 && strnicmp(ptr - 4, " of ", 4) == 0 )
- ptr -= 4;
- else if ( ptr > str + 4 && strnicmp(ptr - 5, "Patch", 5) == 0 )
- {
- ptr -= 5;
- goto label;
- }
- else if ( ptr > str + 5 && strnicmp(ptr - 6, "Patch ", 6) == 0 )
- {
- ptr -= 6;
- goto label;
- }
- else
- return 0; /* header is not part of a multipart subject */
-
- /* kill Patch version */
- if ( ptr > str && 'a' <= ptr[-1] && ptr[-1] <= 'z' )
- ptr--;
-
- /* kill first part number */
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- /* abort if there isn't a part number to the string */
- if ( !isdigit(*ptr) )
- return 0; /* header is not part of a multipart subject */
-
- /* kill opening bracket */
- if ( ptr > str && (ptr[-1] == '(' || ptr[-1] == '[') )
- ptr--;
-
- /* kill spaces before bracket */
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str + 3 && strnicmp(ptr - 4, "Part", 4) == 0 )
- ptr -= 4;
-
- label:
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str && ptr[-1] == ',' )
- ptr--;
- else if ( ptr > str && ptr[-1] == ':' )
- ptr--;
-
- /* *ptr = 0; */ /* truncate the new string */
-
- return (int) (ptr-str); /* return length of string without part numbers */
- }
-
-
- /*-------------------- strip off volume and issue numbers -------------------*/
- static char *skip_vi(char *str)
- {
- char *ptr = str;
-
- /* skip things like "v02i0027: "
- returns pointer to the substring without the volume/issue number */
-
- while ( isspace(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'v' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'i' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != ':' )
- return str;
-
- if ( *ptr++ != ' ' )
- return str;
-
- while ( isspace(*ptr) )
- ptr++;
-
- return ptr;
- }
-
- /*------------- compare multipart headers using some intelligence -----------*/
- int smartcmp(char *str1, char *str2)
- {
- int no_part_len1, no_part_len2;
-
- /* Returns 0 if str1 and str2 are the same except for Part numbers
- Returns <> 0 if they aren't */
-
- if (((no_part_len1 = strip_off_part(str1)) != 0) &&
- ((no_part_len2 = strip_off_part(str2)) != 0)) {
- if (no_part_len1 == no_part_len2) {
-
- /* strings are part of multipart subjects */
- return strnicmp(skip_vi(str1), skip_vi(str2), no_part_len1);
-
- } else
- return 1;
- }
-
- return stricmp(str1, str2);
- }
-
-
- /*------------------------- read the headers --------------------------*/
- ARTICLE *get_headers(ACTIVE *gp)
- {
- /*
- * Read the files, get the headers and add them to the linked list
- */
-
- char *fn;
- char buf[256], fnx[256], *buf_p;
- long g, n_read;
- FILE *tmp_file;
-
- ARTICLE *start, *that, *tmp;
- ART_ID *art_this, *new;
- int ct_art;
-
- n_read = 0;
- ct_art = 0;
- start = NULL;
-
- fn = make_news_group_name(gp->group);
- sprintf(fnx, "%s.IDX", fn);
-
- gotoxy(1,25);
- cprintf("Articles processed: ");
-
- if ((tmp_file = flockopen(fnx, "rb")) != NULL) {
-
- for (g = gp->lo_num+1; g <= gp->hi_num; g++) {
-
- if ((n_read++ % 10) == 0) {
- gotoxy(21,25);
- cprintf("%d", n_read-1); /* # articles processed */
- }
-
- /*
- * Read the index
- * Search the linked list for the subject
- * - allocate a new subject if necessary
- * - add to the existing list
- */
-
- if (fgets(buf, 255, tmp_file) == NULL) {
- gotoxy(1,25);
- fprintf(stderr, "\nsnews: index file is corrupt\n");
- exit(1);
- }
-
- /* check all is in sync */
- if (g != atol(buf+9)) {
- gotoxy(1,25);
- fprintf(stderr, "\nsnews: article %ld found when %ld expected\n", atol(buf+9), g);
- exit(1);
- }
-
- /* skip the two eight digit numbers and the 9 and the spaces */
- buf_p = buf+28;
-
- eat_gunk(buf_p);
- tmp = start;
- while (tmp != NULL) {
- if (smartcmp(buf_p, tmp->header) == 0)
- break;
- tmp = tmp->next;
- }
-
- if (tmp != NULL) {
-
- /* allocate new article number */
- new = xmalloc(sizeof (ART_ID));
- new->id = g;
- new->art_off = atol(buf);
- new->next_art = NULL;
- new->prev_art = NULL;
- tmp->num_articles++;
-
- /* place it at the end */
- art_this = tmp->art_num;
- while (art_this->next_art != NULL) {
- art_this = art_this->next_art;
- }
-
- art_this->next_art = new;
- new->prev_art = art_this;
- }
- else {
-
- /* not found - allocate new thread */
- if (start == NULL) {
- start = that = xmalloc(sizeof (ARTICLE));
- start->last = NULL;
- start->next = NULL;
- start->index = ct_art;
- } else {
- ct_art++;
- that->next = xmalloc(sizeof (ARTICLE));
- that->next->last = that;
- that = that->next;
- that->next = NULL;
- that->index = ct_art;
- }
-
- /* store article data */
- strcpy(that->header, buf_p);
- that->num_articles = 1;
- that->art_num = xmalloc(sizeof (ART_ID));
- that->art_num->next_art = NULL;
- that->art_num->prev_art = NULL;
- that->art_num->id = g;
- that->art_num->art_off = atol(buf);
-
- }
-
- that->next = NULL;
-
- }
-
- fclose(tmp_file);
-
- }
- else {
- gotoxy(1,25);
- fprintf(stderr, "\nsnews: can't open index file %s\n", fnx);
- exit(1);
- }
-
- return(start);
- }
-
-
-
- /*------------------------ clean up subject line ----------------------------*/
- void eat_gunk(char *buf)
- {
- /*
- * This routine take the header line, and strips the
- * word header word, 'Re:', and any extra blanks, trim to 62 chars
- */
-
- char *p = buf;
-
- buf[61] = 0;
-
- while (*p && isspace(*p)) p++;
-
- while (*p && !strnicmp(p, "re:", 3)) {
- p += 3;
- while (*p && isspace(*p)) p++;
- }
-
- if (!p)
- strcpy(buf, "<no subject>");
- else
- strcpy(buf, p);
-
- p = buf + strlen(buf) - 1;
- while (*p && (*p == '\n' || *p == '\t' || *p == '\r'))
- p--;
- p++; *p = '\0';
- }
-
-
- /*-------------------- release the subject structures ---------------------*/
- void free_header(ARTICLE *start)
- {
- /*
- * Work our way through the subject structure releasing all the
- * memory
- */
-
- ARTICLE *a, *t;
- ART_ID *u, *v;
-
- a = start;
-
- while (a != NULL) {
-
- u = a->art_num;
- while (u != NULL) {
- v = u->next_art;
- free(u);
- u = v;
- }
-
- t = a->next;
- free(a);
- a = t;
- }
- }
-
-
- /*------------------- count unread articles in a thread --------------------*/
- int count_unread_in_thread(ACTIVE *gp, ARTICLE *a)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- ART_ID *id;
- int ct, idx;
-
- ct = 0;
- id = a->art_num;
-
- if (gp->read_list)
- while (id != NULL) {
- idx = (int)(id->id - gp->lo_num - 1);
- if (*((gp->read_list)+idx) == FALSE)
- ct++;
- id = id->next_art;
- }
-
- return(ct);
- }
-
-
-
- /*------------------- count unread articles in a group --------------------*/
- int count_unread_in_group(ACTIVE *gp)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- int articles, ct, i;
-
- ct = 0;
- articles = (int)(gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++) {
- if (*((gp->read_list)+i) == FALSE)
- ct++;
- }
-
- return(ct);
- }
-
-
-
- /*------------------ mark all articles in a group as read -----------------*/
- int mark_group_as_read(ACTIVE *gp)
- {
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- * Return nonzero if user aborts
- */
-
- int articles, i, ch;
-
- message("Mark all articles as read (y/n)? ");
- while (((ch = tolower(getch())) != 'y') && (ch != 'n') && (ch != ESCAPE));
-
- if (ch == 'y') {
-
- articles = (int)(gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++)
- *((gp->read_list)+i) = TRUE;
-
- } else
- return 1;
-
- return 0;
- }
-
-
-
- /*-------------------------- status message ---------------------------------*/
- void message(char *msg)
- {
- int x;
-
- x = 40 - (strlen(msg)/2);
-
- gotoxy(1,24);
- if (msg[0]) {
- textbackground(msgb); textcolor(msgf);
- } else { /* blank line to clear message */
- textbackground(textb); textcolor(textf);
- }
- clreol();
- gotoxy(x,24);
- cprintf("%s", msg);
- textbackground(textb); textcolor(textf);
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void lmessage(char *msg)
- {
- gotoxy(1,24);
- textbackground(msgb); textcolor(msgf);
- clreol();
- cprintf("%s", msg);
- textbackground(textb); textcolor(textf);
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void command(char *msg)
- {
- int x;
-
- x = 40 - (strlen(msg)/2);
- gotoxy(1,25);
- textbackground(msgb); textcolor(msgf);
- clreol();
-
- gotoxy(x,25);
- cprintf(msg);
-
- textbackground(textb); textcolor(textf);
- }
-
- /*------------------ get any key including function keys --------------------*/
- int get_any_key(void)
- {
- int result;
-
- if ((result = getch()) == 0)
- result = getch() << 8;
- return result;
- }
-
- /*-------------- search for substring in string ignoring case ---------------*/
- /* modified from str_casefind() in Panagiotis Tsirigotis' strlib library */
- char *stristr(char *str, char *sstr)
- {
- int ssfc = *sstr++ ; /* sub-string first char */
-
- if ( ssfc == 0 )
- return( str ) ;
-
- ssfc = tolower( ssfc ) ;
-
- while ( *str )
- {
- char *current = str ;
- register int strc = *str++ ;
- char *sp ; /* string pointer */
- char *ssp ; /* sub-string pointer */
-
- if ( (strc = tolower(strc)) != ssfc )
- continue ;
-
- for ( sp = str, ssp = sstr ;; sp++, ssp++ )
- {
- register int sc = *sp ; /* string char */
- register int ssc = *ssp ; /* substring char */
-
- /*
- * End-of-substring means we got a match
- */
- if ( ssc == 0 )
- return( current ) ;
-
- /*
- * Convert to lower case if alphanumeric
- */
- if ( (sc = tolower(sc)) != (ssc = tolower(ssc)))
- break ;
- }
- }
-
- return( NULL ) ;
- }
-
- /* end */